package com.sam4cloud.generate.config.v3_5_2;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.IFill;
import com.baomidou.mybatisplus.generator.config.ConstVal;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.TemplateType;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.fill.Column;
import com.sam4cloud.common.core.cons.MicroCloudCons;
import com.sam4cloud.generate.enums.LocationEnum;
import com.sam4cloud.generate.enums.SeniorityEnum;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: Sam
 * @Date: 2022/11/7 16:26
 * @Description: 代码生成器父类
 */
public class SuperGenerator {
    /**
     *         <!--generate支持-->
     *         <dependency>
     *             <groupId>com.baomidou</groupId>
     *             <artifactId>mybatis-plus-generator</artifactId>
     *             <version>3.5.2</version>
     *         </dependency>
     *         <!--mybatisplus支持-->
     *         <dependency>
     *             <groupId>com.baomidou</groupId>
     *             <artifactId>mybatis-plus</artifactId>
     *             <version>3.5.1</version>
     *         </dependency>
     *         <!--模板引擎 velocity-->
     *         <dependency>
     *             <groupId>org.apache.velocity</groupId>
     *             <artifactId>velocity-engine-core</artifactId>
     *             <version>2.3</version>
     *             <scope>compile</scope>
     *             <optional>true</optional>
     *         </dependency>
     *         <!--模板引擎 freemarker-->
     *         <dependency>
     *             <groupId>org.freemarker</groupId>
     *             <artifactId>freemarker</artifactId>
     *             <version>2.3.31</version>
     *             <scope>compile</scope>
     *             <optional>true</optional>
     *         </dependency>
     */


    /*数据源配置*/
    /**
     * url
     */
    String url = "jdbc:mysql://127.0.0.1:3306/micro_oauth2_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false";
    /**
     * 用户名
     */
    String username = "root";
    /**
     * 密码
     */
    String password = "root";

    /*全局配置*/
    /**
     * 模块名称
     */
    String moduleName = "micro-generate";
    /**
     * 输出目录
     */
    String outputDir = System.getProperty("user.dir");
    /**
     * 作者
     */
    String author = "Sam";
    /**
     * 时间策略
     */
    DateType dateType = DateType.TIME_PACK;
    /**
     * 注释日期
     */
    String commentDate = "yyyy-MM-dd HH:mm:ss";

    /*包配置*/
    /**
     * 实体类包名
     */
    String packageParent = "com.sam4cloud.generate1";
    /**
     * 实体类包名
     */
    String packageParentPrefix = packageParent + ".";
    /**
     * 实体类包名
     */
    String entityPackageName = packageParentPrefix + "model.entity";
    /**
     * mapper接口包名
     */
    String mapperPackageName = packageParentPrefix + "mapper";
    /**
     * service接口包名
     */
    String servicePackageName = packageParentPrefix + "service";
    /**
     * service实现类包名
     */
    String serviceImplPackageName = packageParentPrefix + "service.impl";
    /**
     * controller包名
     */
    String controllerPackageName = packageParentPrefix + "controller";

    /**
     * 各文件的输出路径
     *
     * @return pathInfo
     */
    public Map<OutputFile, String> pathInfo(String packageParent, String packagePrefix, String moduleName, LocationEnum locationEnum) {
        // 基础路径
        String basePath = getModuleName(moduleName, locationEnum);
        // java代码的路径
        String javaPath = "/src/main/java";
        // 资源文件的路径
        String resourcePath = "/src/main/resources";
        packagePrefix = StrUtil.isEmpty(packagePrefix) ? packagePrefix : packagePrefix + "/";
        // java代码包路径 com.sam4cloud.generate.project + .sys/
        String javaPackageParent = "/" + packageParent.replace(".", "/") + "/" + packagePrefix;
        // 资源文件的包路径 /mapper/sys
        String resourcePackageSuffix = "/" + packagePrefix;

        /*存放各文件的项目名（单模块项目不用配置）*/
        String xmlProject = "";
        String entityProject = "";
        String mapperProject = "";
        String serviceProject = "";
        String serviceImplProject = "";
        String controllerProject = "";

        //存放各文件的包路径
        String xmlPackage = "/mapper";
        String entityPackage = "entity";
        String mapperPackage = "mapper";
        String servicePackage = "service";
        String serviceImplPackage = "service/impl";
        String controllerPackage = "controller";

        Map<OutputFile, String> pathInfo = new HashMap<>(6);
        pathInfo.put(OutputFile.xml, basePath + xmlProject + resourcePath + xmlPackage + resourcePackageSuffix);
        pathInfo.put(OutputFile.entity, basePath + entityProject + javaPath + javaPackageParent + entityPackage);
        pathInfo.put(OutputFile.mapper, basePath + mapperProject + javaPath + javaPackageParent + mapperPackage);
        pathInfo.put(OutputFile.service, basePath + serviceProject + javaPath + javaPackageParent + servicePackage);
        pathInfo.put(OutputFile.serviceImpl, basePath + serviceImplProject + javaPath + javaPackageParent + serviceImplPackage);
        pathInfo.put(OutputFile.controller, basePath + controllerProject + javaPath + javaPackageParent + controllerPackage);
        return pathInfo;
    }


    /**
     * 获取根目录
     *
     * @param seniority
     * @param moduleName
     * @return
     */
    private String getRootPath(SeniorityEnum seniority, String moduleName) {
        String path;
        switch (seniority) {
            case MOGUL:
                //生成到项目中（大佬采用此法）
                path = System.getProperty("user.dir");
                break;
            case NOVICE:
                //生成到D盘根目录（新手推荐的用法）
                path = "D:";
                break;
            case MODULE:
                //生成到指定目录
                path = System.getProperty("user.dir").replace("micro-generate", MicroCloudCons.PACKAGE + "/" + moduleName);
                break;
            default:
                throw new RuntimeException("Error: This generator method is not supported.");
        }
        return path;
    }

    /*模板配置*/
    /**
     * 禁用模板 本项目配置不生成 Service ServiceImpl Controller
     */
    TemplateType[] disableTemplateType = {
            //TemplateType.ENTITY,
            //TemplateType.MAPPER,
            //TemplateType.XML,
            //TemplateType.SERVICE,
            //TemplateType.SERVICEIMPL,
            //TemplateType.CONTROLLER
    };

    /*策略配置*/
    /**
     * 需要生成的表名
     */
    String[] tableNames = {"oauth_user"};
    /**
     * 过滤表名前缀
     */
    String tablePrefix = "";
    /**
     * 过滤表名后缀
     */
    String tableSuffix = "";
    /**
     * 过滤表字段后缀
     */
    String fieldPrefix = "";
    /**
     * 过滤表字段后缀
     */
    String fieldSuffix = "";

    /*Controller 策略*/
    /**
     * 设置 Controller 父类
     */
    String controllerSuperClass = "";
    /**
     * 格式化 Controller 接口名称。%s:表名。默认是"%sController"
     */
    String formatControllerName = "%sController";

    /*Service 策略*/
    /**
     * 设置 Service 父类
     */
    String superServiceClass = "";
    /**
     * 设置 ServiceImpl 父类
     */
    String superServiceImplClass = "";
    /**
     * 格式化 Service 名称
     */
    String formatServiceFileName = "%sService";
    /**
     * 格式化 ServiceImpl 名称
     */
    String formatServiceImplFileName = "%sServiceImpl";

    /*mapper接口以及xml文件策略*/
    /**
     * 实体类父类的全名
     */
    String mapperSuperClass = "";
    /**
     * 格式化mapper接口名称。%s:表名。默认是"%sMapper"
     */
    String formatMapperFileName = "%sMapper";
    /**
     * 格式化xml文件名称。%s:表名。默认是"%sMapper"
     */
    String formatXmlFileName = "%sMapper";

    /*entity 策略*/
    /**
     * 实体类父类的全名
     */
    String entitySuperClass = "com.sam4cloud.common.core.model.supers.Convert";
    /**
     * 乐观锁字段名(数据库)
     */
    String versionColumnName = "";
    /**
     * 乐观锁属性名(实体)
     */
    String versionPropertyName = "";
    /**
     * 逻辑删除字段名(数据库)
     */
    String logicDeleteColumnName = "del_flag";
    /**
     * 逻辑删除属性名(实体)
     */
    String logicDeletePropertyName = "delFlag";
    /**
     * 添加父类公共字段
     */
    String[] superEntityColumns = {};
    /**
     * 添加忽略字段
     */
    String[] ignoreColumns = {};
    /**
     * 自动填充的字段和时机
     */
    List<IFill> fillColumns = new ArrayList<IFill>() {{
        add(new Column("create_time", FieldFill.INSERT));
        add(new Column("create_date", FieldFill.INSERT));
        add(new Column("update_time", FieldFill.INSERT_UPDATE));
        add(new Column("update_date", FieldFill.INSERT_UPDATE));
    }};
    /**
     * 格式化实体类名称。%s:表名。默认是"%s"
     */
    String formatFileName = "%s";


    /**
     * @param author        作者
     * @param url           数据库连接地址
     * @param username      数据库用户名
     * @param password      数据库密码
     * @param moduleName    模块名 如：micro-system
     * @param locationEnum  内部/外部项目
     * @param packageParent 包跟路径 如：com.sam4cloud.system.project
     * @param packagePrefix 路径包前缀 不填则为不分包 效果 如：输入sys -> project.sys/mapper.sys
     * @param tablePrefix   表前缀 输入前缀则自动去掉表名前缀 如：sys_
     * @param tableNames    表名 如：sys_user
     */
    public void generate(String author, String url, String username, String password, String moduleName,
                         LocationEnum locationEnum, String packageParent, String packagePrefix,
                         String tablePrefix, String... tableNames) {
        // 生成器
        FastAutoGenerator.create(url, username, password)
                // 全局配置
                .globalConfig(builder -> builder
                        .disableOpenDir()                   // 禁止打开输出目录
                        .outputDir(getModuleName(moduleName, locationEnum) + "/src/main/java")               // 指定输出目录
                        .author(author)                     // 设置作者
                        .dateType(dateType)                 // 时间策略
                        .commentDate(commentDate)           // 注释日期
                        .enableSwagger()                    // 开启 swagger 模式
                        .build())
                // 包配置
                .packageConfig(builder -> builder
                        .parent(packageParent + (StrUtil.isEmpty(packagePrefix) ? "" : "." + packagePrefix))  // 包跟路径
                        //.controller(controllerPackageName)      // Controller 包名
                        //.service(servicePackageName)            // Service 包名
                        //.serviceImpl(serviceImplPackageName)    // ServiceImpl 包名
                        //.mapper(mapperPackageName)              // Mapper 包名
                        //.entity(entityPackageName)              // Entity 包名
                        //.xml("mapper")
                        // 设置总路径
                        .pathInfo(pathInfo(packageParent, packagePrefix, moduleName, locationEnum))
                        .build())
                // 模板配置
                .templateConfig(builder -> builder
                        .disable(disableTemplateType)
                        //自定义vm模板
                        .entity("/templates/v3_5_2/entity.java")
                        .entityKt("/templates/v3_5_2/entity.kt")
                        .controller("/templates/v3_5_2/controller.java")
                        .service("/templates/v3_5_2/service.java")
                        .serviceImpl("/templates/v3_5_2/serviceImpl.java")
                        .mapper("/templates/v3_5_2/mapper.java")
                        .xml("/templates/v3_5_2/mapper.xml")
                        .build()
                )
                // 策略配置
                .strategyConfig(builder -> builder
                        .addInclude(tableNames)         // 设置需要生成的表名
                        .addTablePrefix(tablePrefix)    // 设置过滤表名前缀
                        .addTableSuffix(tableSuffix)    // 设置过滤表名后缀
                        .addFieldPrefix(fieldPrefix)    // 设置过滤表字段前缀
                        .addFieldSuffix(fieldSuffix)    // 设置过滤表字段后缀

                        // Controller 策略
                        .controllerBuilder()
                        .fileOverride()                         // 覆盖已生成文件
                        .superClass(controllerSuperClass)       // Controller父类
                        .enableRestStyle()                      // 开启生成 @RestController 控制器
                        .formatFileName(formatControllerName)   // 格式化 Controller 接口名称。%s:表名。默认是"%sController"

                        // Service 策略
                        .serviceBuilder()
                        // 覆盖已生成文件
                        .fileOverride()
                        // 设置 service 接口父类
                        .superServiceClass(StrUtil.isEmpty(superServiceClass) ? ConstVal.SUPER_SERVICE_CLASS : superServiceClass)
                        // 设置 service 实现类父类
                        .superServiceImplClass(StrUtil.isEmpty(superServiceImplClass) ? ConstVal.SUPER_SERVICE_IMPL_CLASS : superServiceImplClass)
                        .formatServiceFileName(formatServiceFileName)           // 格式化 service 接口名称。%s:表名。默认是"I%sService"
                        .formatServiceImplFileName(formatServiceImplFileName)   // 格式化 service 实现类名称。%s:表名。默认是"%sServiceImpl"

                        // mapper策略
                        .mapperBuilder()
                        .fileOverride()                             // 覆盖已生成文件
                        // mapper接口的父类
                        .superClass(StrUtil.isEmpty(mapperSuperClass) ? ConstVal.SUPER_MAPPER_CLASS : mapperSuperClass)
                        .enableMapperAnnotation()                   // 开启 @Mapper 注解
                        .enableBaseResultMap()                      // 启用 BaseResultMap 生成
                        .enableBaseColumnList()                     // 启用 BaseColumnList
                        .formatMapperFileName(formatMapperFileName) // 格式化mapper接口名称。%s:表名。默认是"%sMapper"
                        .formatXmlFileName(formatXmlFileName)       // 格式化xml文件名称。%s:表名。默认是"%sMapper"

                        // entity 策略
                        .entityBuilder()
                        .fileOverride()                                     // 覆盖已生成文件
                        .idType(IdType.ASSIGN_UUID)                         // 主键策略  雪花算法自动生成的id
                        .superClass(entitySuperClass)                       // 实体类的父类
                        .enableLombok()                                     // 开启 Lombok 注解
                        //.disableSerialVersionUID()                          // 禁用序列化
                        //.enableChainModel()                                 // 生成setter方法返回this
                        //.enableTableFieldAnnotation()                       // 开启生成实体时生成字段注解
                        //.enableActiveRecord()                               // 开启 ActiveRecord 模型 如：class OauthUser extends Convert<OauthUser>
                        .versionColumnName(versionColumnName)               // 乐观锁字段名(数据库)
                        .versionPropertyName(versionPropertyName)           // 乐观锁属性名(实体)
                        .logicDeleteColumnName(logicDeleteColumnName)       // 逻辑删除字段名(数据库)
                        .logicDeletePropertyName(logicDeletePropertyName)   // 逻辑删除属性名(实体)
                        .addSuperEntityColumns(superEntityColumns)          // 添加父类公共字段
                        .addIgnoreColumns(ignoreColumns)                    // 添加忽略字段
                        .addTableFills(fillColumns)                         // 自动填充字段
                        .formatFileName(formatFileName)                     // 格式化实体类名称。%s:表名。默认是"%s"

                        .build())
                //.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板，默认的是Velocity引擎模板
                .execute();
    }

    /**
     * 获取模块路径
     *
     * @param moduleName
     */
    private String getModuleName(String moduleName, LocationEnum locationEnum) {
        if (LocationEnum.INNER.equals(locationEnum)) {
            return StrUtil.isEmpty(moduleName) ? outputDir : outputDir.replace(this.moduleName, MicroCloudCons.PACKAGE + "/" + moduleName);
        } else {
            return StrUtil.isEmpty(moduleName) ? outputDir : outputDir.replace(this.moduleName, moduleName);
        }
    }

}